package cn.accjiyun.gomoku.view;

import cn.accjiyun.gomoku.MainApp;
import cn.accjiyun.gomoku.model.*;
import cn.accjiyun.gomoku.util.Logger;
import cn.accjiyun.gomoku.view.resources.MyShadow;
import javafx.fxml.FXML;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.FileChooser;

import java.io.*;
import java.util.Stack;

/**
 * Created by jiyun on 2016/9/7.
 */
public class RootLayoutController {

    // Reference to the main application
    private MainApp mainApp;
    @FXML
    BorderPane borderPane;
    @FXML
    CheckMenuItem itemBlack;
    @FXML
    CheckMenuItem itemWhite;
    @FXML
    CheckMenuItem itemShowLog;
    @FXML
    CheckMenuItem itemShowNum;

    private boolean isShowNum;
    private boolean isShowLog;

    private Canvas canvas;
    private double width;
    private double height;

    public boolean isShowNum() {
        return isShowNum;
    }

    public RootLayoutController() {
        isShowNum = true;
        isShowLog = true;
    }

    /**
     * Is called by the main application to give a reference back to itself.
     *
     * @param mainApp
     */
    public void setMainApp(MainApp mainApp) {
        this.mainApp = mainApp;
    }


    /**
     * According to the chess board size to adjust window size.
     */
    public void adjustStageSize() {
        double size = mainApp.getChessboard().getBoardSize() - 1;
        double step = 40;
        borderPane.setMinHeight(size * step + 120);
        borderPane.setMinWidth(size * step + 540);
    }

    /**
     * Called to create a new game.
     */
    @FXML
    private void handleNew() {
        mainApp.getChessboard().clear();
        adjustStageSize();
        mainApp.getBoardLayoutController().reDrawChess();
        mainApp.getBoardLayoutController().clearLog();
        mainApp.getBoardLayoutController().shouldThink();
    }

    /**
     * Opens a dialog to load the backup file.
     *
     * @return true if user selected file correct.
     */
    @FXML
    private boolean handleLoad() {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("选择备份文件");
        fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Save File", "*.save"));
        File file = fileChooser.showOpenDialog(mainApp.getPrimaryStage());
        if (file != null) {
            openFile(file);
        }
        return false;
    }

    private void openFile(File file) {
        mainApp.getChessboard().clear();
        Stack<ChessPoint> chessStack = mainApp.getChessboard().getChessStack();
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(file));
            mainApp.getChessboard().setBoardSize(Integer.valueOf(reader.readLine()));
            int step = Integer.valueOf(reader.readLine());
            String line;
            while (step-- > 0 && (line = reader.readLine()) != null) {
                String[] coordinate = line.split(" ");
                ChessPoint chessPoint = new ChessPoint();
                chessPoint.setX(Integer.valueOf(coordinate[0]));
                chessPoint.setY(Integer.valueOf(coordinate[1]));
                chessPoint.setRole(Integer.valueOf(coordinate[2]));
                chessStack.push(chessPoint);
            }
            mainApp.getChessboard().setMode(Integer.valueOf(reader.readLine()));
            String[] sLevel = reader.readLine().split(" ");
            int iLevel = Integer.valueOf(sLevel[0]);
            DialogController.setLevel(iLevel);
            if (iLevel == EngineLevel.CUSTOM) {
                EngineLevel.setLevel(iLevel, Integer.valueOf(sLevel[1]), Integer.valueOf(sLevel[2]));
            } else {
                EngineLevel.setLevel(iLevel);
            }
            String [] showInfo = reader.readLine().split(" ");
            isShowNum = Boolean.valueOf(showInfo[0]);
            isShowLog = Boolean.valueOf(showInfo[1]);
            DialogController.reOpenWindow(mainApp);
            reFreshMenu();
            mainApp.getBoardLayoutController().checkWin();
            mainApp.getBoardLayoutController().shouldThink();
        } catch (FileNotFoundException e) {
            Logger.exceptionLog("Fail to find the open file", e);
        } catch (IOException e) {
            Logger.exceptionLog("Fail to find the open file", e);
        } finally {
            try {
                reader.close();
            } catch (IOException e) {
                Logger.exceptionLog("Fail to close reader Stream", e);
            }
        }
    }

    /**
     * Opens a dialog to save the chess board data in a file.
     *
     * @return true if save successful.
     */
    @FXML
    private boolean handleSave() {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setTitle("保存棋盘记录");
        fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Save File", "*.save"));
        File file = fileChooser.showSaveDialog(mainApp.getPrimaryStage());
        if (file != null) {
            saveFile(file);
        }
        return false;
    }

    private void saveFile(File file) {
        FileWriter fileWriter = null;
        BufferedWriter bufferedWriter = null;
        try {
            fileWriter = new FileWriter(file.getAbsoluteFile());
            bufferedWriter = new BufferedWriter(fileWriter);
            bufferedWriter.write(mainApp.getChessboard().getBoardSize() + "\r\n");
            Stack<ChessPoint> chessStack =  mainApp.getChessboard().getChessStack();
            bufferedWriter.write(chessStack.size() + "\r\n");
            for(ChessPoint chessPoint : chessStack) {
                bufferedWriter.write(chessPoint.getX() + " " + chessPoint.getY() + " " + chessPoint.getRole() + "\r\n");
            }
            bufferedWriter.write(mainApp.getChessboard().getMode() + "\r\n");
            bufferedWriter.write(String.valueOf(DialogController.getLevel()));
            if (DialogController.getLevel() == EngineLevel.CUSTOM) {
                bufferedWriter.write(" " + EngineLevel.saveDepth + " " + EngineLevel.saveWidth + "\r\n");
            } else {
                bufferedWriter.newLine();
            }
            bufferedWriter.write(isShowNum + " " + isShowLog + "\r\n");
            bufferedWriter.flush();
        } catch (IOException e) {
            Logger.exceptionLog("Fail to save chess data", e);
        } finally {
            try {
                bufferedWriter.close();
                fileWriter.close();
            } catch (IOException e) {
                Logger.exceptionLog("Fail to close writer Stream", e);
            }
        }

    }

    /**
     * Opens a dialog to edit size for the chess board.
     *
     * @return true if the the number meet the requirement.
     */
    @FXML
    private boolean handleResize() {
        DialogController.show(mainApp.getPrimaryStage(), DialogType.EDITSIZE, mainApp);
        DialogController.getDialogController().initEditSizeDialog();
        return false;
    }

    /**
     * Closes the application.
     */
    @FXML
    private void handleExit() {
        width = mainApp.getPrimaryStage().getWidth();
        height = mainApp.getPrimaryStage().getHeight();
        canvas = new Canvas(width, height);
        borderPane.getChildren().add(canvas);
        GraphicsContext gc = canvas.getGraphicsContext2D();
        gc.setFill(Color.WHITE);
        gc.fillRoundRect(0, 0, width, height, 0, 0);
        gc.setFill(Color.YELLOWGREEN);
        gc.setEffect(MyShadow.RED);
        gc.setFont(new Font("Microsoft YaHei", 60));
        gc.fillText("欢迎再次使用！", width / 2 - 200, height / 2 - 30);

        new Thread() {
            @Override
            public void run() {
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    gc.fillText("退出失败，请关闭此程序！", width / 2 - 200, height / 2 + 20);
                    Logger.exceptionLog("Fail to exit application", e);
                    Thread.currentThread().interrupt();
                }
                System.exit(0);
            }
        }.start();
    }

    private void reFreshMenu() {
        switch (mainApp.getChessboard().getMode()) {
            case Mode.DOUBLE :
                itemBlack.setSelected(true);
                itemWhite.setSelected(true);
                break;
            case Mode.BLACK:
                itemBlack.setSelected(true);
                itemWhite.setSelected(false);
                break;
            case Mode.White:
                itemBlack.setSelected(false);
                itemWhite.setSelected(true);
                break;
            case Mode.EMPTY:
                itemBlack.setSelected(false);
                itemWhite.setSelected(false);
        }
        if (!isShowNum) itemShowNum.setSelected(false);
        if (!isShowLog) itemShowLog.setSelected(false);
        mainApp.getBoardLayoutController().showLog(isShowLog);
    }

    /**
     * Change the Mode
     */
    @FXML
    private void handleChangeMode() {
        Chessboard chessboard = mainApp.getChessboard();
        if (itemBlack.isSelected() && itemWhite.isSelected()) {
            chessboard.setMode(Mode.DOUBLE);
        } else if (itemBlack.isSelected()) {
            chessboard.setMode(Mode.BLACK);
        } else if (itemWhite.isSelected()) {
            chessboard.setMode(Mode.White);
        } else {
            chessboard.setMode(Mode.EMPTY);
        }
        mainApp.getBoardLayoutController().shouldThink();
    }

    /**
     * Open Dialog to set the AI level.
     */
    @FXML
    private void handleSetLevel() {
        DialogController.show(mainApp.getPrimaryStage(), DialogType.EDITLEVEL, mainApp);
        DialogController.getDialogController().initEditLevelDialog();
    }

    /**
     * Show number on chess if the checkMenuItem has been selected.
     */
    @FXML
    private void handleShowNum() {
        isShowNum = !isShowNum;
        mainApp.getBoardLayoutController().reDrawChess();
    }

    /**
     * Show engine thinking log if the checkMenuItem has been selected.
     */
    @FXML
    private void handleShowLog() {
        isShowLog = !isShowLog;
        mainApp.getBoardLayoutController().showLog(isShowLog);
    }

    /**
     * Opens a Dialog to show the information about the application.
     */
    @FXML
    private void handleAbout() {
        DialogController.show(mainApp.getPrimaryStage(), DialogType.CONFIRM, mainApp);
    }
}